/*
 * This file is part of LazyCipher. LazyCipher is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version. LazyCipher is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details. You should have received a
 * copy of the GNU General Public License along with LazyCipher. If not, see
 * <http://www.gnu.org/licenses/>.
 */
package org.lazycoder.Crypto.iface;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.AbstractAction;
import javax.swing.AbstractListModel;
import javax.swing.Action;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

import org.lazycoder.Crypto.Message;
import org.lazycoder.Crypto.Ciphers.Cipher;
import org.lazycoder.Crypto.Keys.Key;
import org.lazycoder.Crypto.Keys.TextKey;

/**
 * @author Matthew Bayliss
 */
public class Main {

	private JFrame				frmLazyCipher;
	private final JPanel		panelCipher		= new JPanel();
	private final JTextPane		textName		= new JTextPane();
	private final JTextPane		textDescription	= new JTextPane();
	private final JList<String>	listCiphers		= new JList<String>();
	private final Action		changeCipher	= new ChangeCipher();
	private final Action		encrypt			= new Encrypt();
	private final Action		showDescription	= new ShowDescription();
	private final Action		decrypt			= new Decrypt();
	private final JTextArea		textMessage		= new JTextArea();
	private final JTextArea		textKey			= new JTextArea();
	private PlainDocument		key;
	private PlainDocument		message;

	private Cipher				cipher			= null;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			@Override
			public void run() {
				try {
					Main window = new Main();
					window.frmLazyCipher.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public Main() {
		initialize();
		listCiphers.setSelectedIndex(0);
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedLookAndFeelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		frmLazyCipher = new JFrame();
		frmLazyCipher.setTitle("Lazy Cipher");
		frmLazyCipher.setBounds(100, 100, 500, 600);
		frmLazyCipher.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

		key = new PlainDocument();
		textKey.setWrapStyleWord(true);
		textKey.setLineWrap(true);
		textKey.setDocument(key);
		message = new PlainDocument();
		textMessage.setDocument(message);

		listCiphers.addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent arg0) {
				String ct = listCiphers.getSelectedValue();
				changeCipher
						.actionPerformed(new ActionEvent(listCiphers, 1, ct));
			}
		});
		listCiphers.setModel(new AbstractListModel<String>() {
			String[]	values	= new String[] { "Caesar", "Vigenere" };

			@Override
			public int getSize() {
				return values.length;
			}

			@Override
			public String getElementAt(int index) {
				return values[index];
			}
		});
		listCiphers.setPreferredSize(new Dimension(100, 0));
		frmLazyCipher.getContentPane().add(listCiphers, BorderLayout.WEST);

		panelCipher.setVisible(false);
		panelCipher.setBorder(new EmptyBorder(0, 2, 0, 0));
		frmLazyCipher.getContentPane().add(panelCipher, BorderLayout.CENTER);
		panelCipher.setLayout(new BorderLayout(0, 0));

		JPanel panelDescription = new JPanel();
		panelDescription.setPreferredSize(new Dimension(10, 100));
		panelCipher.add(panelDescription, BorderLayout.NORTH);
		panelDescription.setLayout(new BorderLayout(0, 0));
		textName.setFont(textName.getFont().deriveFont(
				textName.getFont().getStyle() | Font.BOLD,
				textName.getFont().getSize() + 5f));
		textName.setBackground(SystemColor.control);
		textName.setEditable(false);
		panelDescription.add(textName, BorderLayout.NORTH);

		JScrollPane scrollDescription = new JScrollPane();
		scrollDescription.setBorder(null);
		panelDescription.add(scrollDescription, BorderLayout.CENTER);
		textDescription.setEditable(false);
		textDescription.setBackground(SystemColor.control);
		scrollDescription.setViewportView(textDescription);

		JPanel panelControls = new JPanel();
		panelCipher.add(panelControls, BorderLayout.SOUTH);
		panelControls.setLayout(new GridLayout(0, 3, 0, 0));

		JButton btnEncrypt = new JButton("Encrypt");
		btnEncrypt.setAction(encrypt);
		panelControls.add(btnEncrypt);

		JButton btnDescription = new JButton("Description");
		btnDescription.setEnabled(false);
		btnDescription.setAction(showDescription);
		panelControls.add(btnDescription);

		JButton btnDecrypt = new JButton("Decrypt");
		btnDecrypt.setAction(decrypt);
		panelControls.add(btnDecrypt);

		JPanel panelIO = new JPanel();
		panelIO.setBorder(new EmptyBorder(2, 0, 2, 0));
		panelCipher.add(panelIO, BorderLayout.CENTER);
		panelIO.setLayout(new GridLayout(0, 2, 2, 2));

		JPanel panelMessage = new JPanel();
		panelIO.add(panelMessage);
		panelMessage.setLayout(new GridLayout(1, 0, 0, 0));
		textMessage.setWrapStyleWord(true);
		textMessage.setLineWrap(true);
		panelMessage.add(textMessage);

		JPanel panelKey = new JPanel();
		panelIO.add(panelKey);
		panelKey.setLayout(new BorderLayout(0, 0));

		JComboBox<String> comboKeyType = new JComboBox<String>();
		comboKeyType.setEnabled(false);
		comboKeyType.addPropertyChangeListener(new PropertyChangeListener() {
			@Override
			public void propertyChange(PropertyChangeEvent arg0) {

			}
		});
		comboKeyType.setModel(new DefaultComboBoxModel<String>(new String[] {
				"Integer", "Text" }));
		panelKey.add(comboKeyType, BorderLayout.NORTH);

		panelKey.add(textKey, BorderLayout.CENTER);

		JMenuBar menuBar = new JMenuBar();
		frmLazyCipher.setJMenuBar(menuBar);
	}

	private class ChangeCipher extends AbstractAction {
		public ChangeCipher() {
			putValue(NAME, "Change Cipher");
			putValue(SHORT_DESCRIPTION,
					"Change the active cipher and update the appropriate fields.");
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			Class<?> cip;
			try {
				cip = Class.forName("org.lazycoder.Crypto.Ciphers."
						+ e.getActionCommand());
				cipher = (Cipher) cip.newInstance();
				textName.setText(cipher.getName());
				textDescription.setText(cipher.getDescription());
				textDescription.setCaretPosition(0);
				if (e.getSource() != listCiphers) {
					listCiphers.setSelectedValue(e.getActionCommand(), true);
				}
				panelCipher.setVisible(true);
				// TODO Make better errors.
			} catch (ClassNotFoundException e1) {
				System.err
						.println("Invalid cipher type. No class found with name :\'"
								+ e.getActionCommand() + "\'. ");
				JOptionPane.showMessageDialog(null,
						"The cipher you selected can't be found. ",
						"Invalid cipher", JOptionPane.ERROR_MESSAGE);
			} catch (InstantiationException e1) {
				System.err
						.println("Invalid cipher type. Abstract or interface");
				JOptionPane.showMessageDialog(null,
						"The cipher you selected can't be found. ",
						"Invalid cipher", JOptionPane.ERROR_MESSAGE);
			} catch (IllegalAccessException e1) {
				System.err.println("Invalid cipher type. I can't access it. ");
				JOptionPane.showMessageDialog(null,
						"The cipher you selected can't be found. ",
						"Invalid cipher", JOptionPane.ERROR_MESSAGE);
			}
		}
	}

	private class Encrypt extends AbstractAction {
		public Encrypt() {
			putValue(NAME, "Encrypt");
			putValue(SHORT_DESCRIPTION, "Encrypt the text. ");
			putValue(MNEMONIC_KEY, KeyEvent.VK_E);
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			try {
				Message m = new Message(message.getText(0, message.getLength()));
				Key k = new TextKey(key.getText(0, key.getLength()));

				String out = cipher.encrypt(m, k).toString();
				message.remove(0, message.getLength());
				message.insertString(0, out, null);
			} catch (BadLocationException e1) {
				// TODO Error
			}
		}
	}

	private class ShowDescription extends AbstractAction {
		public ShowDescription() {
			putValue(NAME, "Show Description");
			putValue(SHORT_DESCRIPTION,
					"Show a seperate window with a description of the cipher. ");
		}

		@Override
		public void actionPerformed(ActionEvent e) {}
	}

	private class Decrypt extends AbstractAction {
		public Decrypt() {
			putValue(NAME, "Decrypt");
			putValue(SHORT_DESCRIPTION, "Decrypt the text.");
			putValue(MNEMONIC_KEY, KeyEvent.VK_D);
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			try {
				Message m = new Message(message.getText(0, message.getLength()));
				Key k = new TextKey(key.getText(0, key.getLength()));

				String out = cipher.decrypt(m, k).toString();
				message.remove(0, message.getLength());
				message.insertString(0, out, null);
			} catch (BadLocationException e1) {
				// TODO Error
			}
		}
	}

}
